<?php
if (!class_exists('SecurityManager')) {


	/**
	 * Site security manager
	 * @author Alecsandru Gabriel DINU
	 * @since Jun 15, 2008 
	 */
	class SecurityManager {

		const configFile = "security.xml";
		const configFile_cache = "security_cache.php";
		private $cacheFilename;
		private $context;
		private $patterns = array();
		private $devMode;

		public function __construct(Context $context, $devMode = false) {
			if (!($context instanceof Context)) {
				throw new InvalidArgumentException("$context parameter is not a instance of Context class");
			}
			
			
			$this->context = $context;
			 
			define("ROLE_ANONYMOUS", "anonymous");
			define("ROLE_ADMIN", "admin");
			
			$config = AlfaConfiguration::getInstance();
			$this->cacheFilename = $config->getString('cache/dir') . "/" . self::configFile_cache;
			$this->devMode = $devMode;
			$this->load();
		}

		private function load() {
			if ($this->devMode) { // load the configuration every time
				$this->patterns = $this->loadConfiguration();
			} else { //try to load the configuration from cache
				// Is our cache available? Recreate it!
				if(!is_readable($this->cacheFilename)) {
					$this->writeConfigToFile($this->loadConfiguration());
				}
				require_once $this->cacheFilename;
				$this->patterns =& $GLOBALS['securityConfigurationData'];
			}
		}

		public function reload() {
			$this->patterns = $this->loadConfiguration();
		}

		/**
		 * Load config from xml and store it into
		 * an array
		 *
		 * @return unknown
		 */
		private function loadConfiguration() {
			$xmlStr = FileUtilities::getFileContent(self::configFile);
			$xml = new SimpleXMLElement($xmlStr);

			// Generates a the list with all the actions
			// from all the packages
			$patterns = array();
			foreach ($xml->role as $role) {
				foreach ($role->pattern as $pattern) {
					$patterns[(string)$pattern] = $role["name"];
				}
			}

			return $patterns;
		}

		private function writeConfigToFile($config) {
			/* Generate php cache file */
			$cache_content = "<?php\n// this is a automatically generated cache file.\n\n";
			foreach($config as $pattern => $role) {
				$cache_content .= "\$GLOBALS['securityConfigurationData']['$pattern'] = '$role';\n";
			}
			$cache_content .= "?>";
			if($cacheFilename_handle = fopen($this->cacheFilename, "w+")) {
				fwrite($cacheFilename_handle, $cache_content);
				/* Allow ftp users to access/modify/delete cache file, suppress chmod errors here */
				@chmod($this->cacheFilename, 0664);
			}
		}

		/**
		 * Throws a SecurityException if the requested access, specified by the given permission, 
		 * is not permitted based on the security policy currently in effect.
		 *
		 * @param string $permission
		 * @return boolean
		 */
		public function checkPermission() {
			$current_roles = array();
			if ($this->context->isUserLoggedIn()) {
				// try to get _credentials
				$current_roles = $this->context->getRoles(); 
			}
			$current_roles[] = ROLE_ANONYMOUS;
			
			$role = $this->getRequiredRole($this->context->getAction());
			if (!in_array($role, $current_roles)) {
				throw new SecurityException("User doesn't have required permissions to access this resource");
			}
		}
		
		/**
		 * Gets the required ROLE to access the resource
		 *	TODO: extend this to allow database support
		 * @param string $resource
		 * @return string
		 */
		private function getRequiredRole($resource) {
			foreach ($this->patterns as $pattern => $role) {
				$p = $pattern;
				$p = str_replace('.', '\.', $p);
				$p = str_replace('*', '.', $p);

				if (eregi($p, $resource)) {
					return $role;
				}
			}
				
			return ROLE_ANONYMOUS;
		}		
	}
}